﻿using System;
using System.Globalization;
using System.Configuration;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq.SqlClient;
using System.Text;
using System.Web;
using System.IO;
using System.Net.Mail;
using IntraVision.Web;
using IntraVision.Web.Filter;
using IntraVision.Web.Security;
using Model;
using LZMA;
using EmitMapper;
using EmitMapper.MappingConfiguration;

using NotarialService.Classes;

public class LockOrderInfo
{
    public int id {get; set;}
    public DateTime date {get; set;}
    public int userId {get; set;}
    public string userName {get; set;}
    public LockOrderInfo(int id, DateTime date, int userId, string userName)
    {
        this.id = id;
        this.date = date;
        this.userId = userId;
        this.userName = userName;
    }
}

public struct OrderAction
{
    private string name, action, href;

    public string Name { get {return name;} }
    public string Action { get { return action; } }
    public string Href { get { return href; } }

    public OrderAction(string name, string href, string action)
    {
        this.name = name;
        this.action = action;
        this.href = href;
    }
}

public struct StatusInfo
{
    private int id, ordersCount;
    private string name;

    public int Id { get { return id; } }
    public string Name { get { return name; } }
    public int OrdersCount { get { return ordersCount; } }

    public StatusInfo(int id, string name, int ordersCount)
    {
        this.id = id;
        this.name = name;
        this.ordersCount = ordersCount;
        SubStatuses = new List<StatusInfo>();
    }

    public IList<StatusInfo> SubStatuses;
}

[AllowedRole("IsAdmin")]
public partial class OrderController : TableController<NotarialServiceDataContext, Order>
{
    const int _DefaultRefreshInterval = 20;
    const string delimeter = "|<d>|";

    private static object lockObj = new Object();

    private List<LockOrderInfo> lockedOrdersInfo = null;

    LockOrderInfo lockOrderInfo = null;
    bool create = false;
    int parentTaskId = 0;
    int parentOrderId = 0;
    int clonedOrderId = 0;
    bool createNewAfterSave = false;
    bool editMode = false;
    bool printMode = false;
    bool shouldUnlockOrder = false;

    bool isPaidChanged = false;
    bool statusIdChanged = false;
    bool orderChanged = false;
    bool userCanEditOrder = false;
    bool notarialActionChanged = false;

    public OrderController()
    {
        _ExcludeStateKeys.Add("tb_statusid");
        _FilterIndependentKeys.Add("tb_statusid");
    }

    [AllowedRole("DocumentRecieve")]
    public void Clone(string entityid)
    {
        int entityId;

        if (Int32.TryParse(entityid, out entityId) && entityId > 0)
        {
            clonedOrderId = entityId;
            Create();
        }
        else
            throw new IntraVisionWebException(404);
    }

    protected int _StatusId
    {
        get { return Session["tb_statusid"] != null ? Convert.ToInt32(Session["tb_statusid"]) : 0; }
        set { Session["tb_statusid"] = value.ToString(); }
    }

    protected int _PersonId = 0;
    protected int _CompanyId = 0;

    protected override void EntityLoaded(NotarialServiceDataContext dc, Order entity)
    {
        var user = Session["User"] as User;

        if (user != null && !userCanEditOrder && !editMode && lockedOrdersInfo == null)
            userCanEditOrder = user.CanEditOrder(dc, entity);

        create = (entity.Id == 0);

        //если режим создания на основе записи на прием 
        if (create && parentTaskId > 0)
        {
            var parentTask = dc.Tasks.FirstOrDefault(t => t.Id == parentTaskId);
            if (parentTask != null)
            {
                entity.Description = parentTask.Comment;
                entity.NotarialAction = parentTask.NotarialAction;
                entity.NotarialActionId = parentTask.NotarialActionId;
                entity.TaskId = parentTask.Id;
                ViewData["clone"] = true;
            }
        }

        //если режим создания и задана родительская заявка - скопируем ее поля
        if (create && parentOrderId > 0)
        {
            var parentOrder = dc.Orders.FirstOrDefault(o => o.Id == parentOrderId);
            if (parentOrder != null)
            {
                var mapperManager = new ObjectMapperManager();
                var mapper = mapperManager.GetMapper<Order, Order>();

                mapper.Map(parentOrder, entity);

                entity.Id = 0;
                entity.CopyCount = 0;
            }
        }

        //если задан id заявки для клонирования
        if (create && clonedOrderId > 0)
        {
            var clonedOrder = dc.Orders.FirstOrDefault(o => o.Id == clonedOrderId);
            if (clonedOrder != null)
            {
                entity.Description = clonedOrder.Description;
                entity.NotarialAction = clonedOrder.NotarialAction;
                entity.NotarialActionId = clonedOrder.NotarialActionId;
                entity.TaskId = clonedOrder.TaskId;
                ViewData["clone"] = true;
            }
        }

        ViewData["userCanEditOrder"] = userCanEditOrder;
        if (!editMode && !create) ViewData["readonly"] = "True";
        int roleId = (user != null) ? user.RoleId : 0;

        if (!editMode && !create && lockOrderInfo != null && lockOrderInfo.userId != user.Id)
        {
            ViewData["lockOrderInfo"] = lockOrderInfo;
        }

        ViewData["notarialactions"] = (from na in dc.NotarialActions orderby na.FullName select na).ToList();
        if (!create)
            ViewData["notarialregisters"] = (from nr in dc.NotarialRegisters where !nr.IsArchive orderby nr.Id select nr).ToList();

        var userpermissions = (from up in dc.UserPermissions where up.UserId == user.Id select up.PermissionId).ToList();

        //Определим список доступных статусов, в которые может переходить заявка (по матрице переходов)

        var statuses = new List<Status>(0);

        statuses = (from ws in dc.WorkflowStatusMatrixes
                    where ws.FromStatusId == ((create) ? null : (int?)entity.StatusId)
                        && (!ws.PermissionId.HasValue || userpermissions.Contains(ws.PermissionId.Value))
                    select ws.ToStatus).Distinct().OrderBy(s => s.SortOrder).ToList();

        ViewData["statuses"] = statuses;
        ViewData["marks"] = (from m in dc.Marks select m);

        //период для вычисления срока по-умолчанию
        if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["orderperiod"]))
        {
            int orderperiod;
            Int32.TryParse(ConfigurationManager.AppSettings["orderperiod"], out orderperiod);
            if (orderperiod > -1)
                ViewData["orderperiod"] = orderperiod;
        }

        //список заявителей
        List<IClient> clients = GetOrderClients(dc, entity.Id);//new List<IClient>();
        //clients.AddRange((from cp in dc.OrderClientPersons where cp.OrderId == entity.Id || (parentOrderId > 0 && cp.OrderId == parentOrderId) || (clonedOrderId > 0 && cp.OrderId == clonedOrderId) select (IClient)cp.Person).ToList<IClient>());
        //clients.AddRange((from cc in dc.OrderClientCompanies where cc.OrderId == entity.Id || (parentOrderId > 0 && cc.OrderId == parentOrderId) || (clonedOrderId > 0 && cc.OrderId == clonedOrderId) select (IClient)cc.Company).ToList<IClient>());

        //если создается на основе записи на прием
        if (parentTaskId > 0)
            clients.AddRange((from t in dc.Tasks where t.Id == parentTaskId select (IClient)t.Person).ToList<IClient>());

        ViewData["clients"] = clients.OrderBy(c => c.Name).ToList<IClient>();
        ViewData["clientsids"] = clients.Select(c => c.Cid).ToArray();

        //список поверенных
        List<IClient> responsibles = GetOrderResponsibles(dc, entity.Id);// new List<IClient>();
        // responsibles.AddRange((from rc in dc.OrderResponsibleCompanies where rc.OrderId == entity.Id || (parentOrderId > 0 && rc.OrderId == parentOrderId) || (clonedOrderId > 0 && rc.OrderId == clonedOrderId) select (IClient)rc.Company).ToList<IClient>());
        //responsibles.AddRange((from rp in dc.OrderResponsiblePersons where rp.OrderId == entity.Id || (parentOrderId > 0 && rp.OrderId == parentOrderId) || (clonedOrderId > 0 && rp.OrderId == clonedOrderId) select (IClient)rp.Person).ToList<IClient>());
        ViewData["responsibles"] = responsibles.OrderBy(r => r.Name).ToList<IClient>();
        ViewData["responsibleids"] = responsibles.Select(c => c.Cid).ToArray();

        //история изменений
        if (!create)
            ViewData["history"] = (from oh in dc.OrderHistories where oh.OrderId == entity.Id orderby oh.Date descending select oh).ToList();

        if (!printMode)
        {
            RegisterScript("jquery.multipleselect.js", "table.js", "jquery.autocomplete.js", "jquery.elastic.js", "jquery.blockUI.js", "jquery.notifica.min.js");
            RegisterStylesheet("autocomplete/autocomplete.css", "order/order.css");
        }
        else
        {
            ViewData["notarialsettings"] = (from ns in dc.NotarialSettings select ns).FirstOrDefault();
        }

        //вложенные файлы и документы 
        ViewData["files"] = entity.OrderFiles.Where(f => !f.IsDeleted && !f.IsFinal).ToList();
        PrepareDocTemplateList(entity.NotarialActionId, entity.Id);
        //ViewData["doctemplates"] = entity.OrderDocTemplates.Where(f => !f.IsDeleted).ToList();
        ViewData["versiondocs"] = entity.OrderDocTemplates
                                    .Where(f => f.IsNotTemp && f.TemplateTypeId == (int)OrderDocTemplateType.NotarialDocTemplate)                                    
                                    .ToList();
        ViewData["userversiondocs"] = entity.OrderDocTemplates
                            .Where(f => f.IsNotTemp && f.TemplateTypeId == (int)OrderDocTemplateType.UserDocTemplate)
                            .ToList();
        ViewData["resultfile"] = entity.OrderFiles.Where(f => !f.IsDeleted && f.IsFinal).FirstOrDefault();
        ViewData["activenotarius"] = GetNotariusList(dc);

        ViewData["formnumbers"] = GetNotarialForms(dc, entity);
    }

    protected override void BeforeSubmit(NotarialServiceDataContext dc, Order entity)
    {
        create = (entity.Id == 0);
        editMode = !String.IsNullOrEmpty(Request["editmode"]);

        //если докумнет внутренний, обнуляем все, что касается оплаты
        if (entity.IsInternal)
        {
            entity.CopyPrice = entity.Tax = entity.DocPrice = 0;
            entity.IsCashless = entity.IsPrepayment = entity.IsPaid = false;
        }

        if (entity.IsNotForRegistration)
            entity.NotarialRegisterId = entity.NotarialRegisterRow = null;

        //сохраняем заявителей и поверенных
        SaveClients(dc, entity);
        SaveResponsibles(dc, entity);

        //сохраняем срок выполнения
        if (Request["deadline"] != null && Request["deadlinehours"] != null && Request["deadlineminutes"] != null)
            entity.DeadlinePartsArray = new string[3] { Request["deadline"], Request["deadlinehours"], Request["deadlineminutes"] };

        if (entity.Description != null)
            entity.Description = entity.Description.Trim();

        var gc = dc.GetChangeSet();
        orderChanged = (gc.Deletes.Count > 0 || gc.Inserts.Count > 0 || gc.Updates.Count > 0);

        //определяем и сохраняем тип операции и пользователя
        var operation = GetOperation();

        if (!String.IsNullOrEmpty(operation))
        {
            entity.Operation = operation;
            entity.EditorId = (Session["user"] as User).Id;
        }

        //Сохранить файлы в базу
        UploadFiles(entity, (Session["user"] as User));
        UploadUserVersionFiles(entity, (Session["user"] as User));

        //нажата кнопка "На рабочий стол + копия" 
        if (!String.IsNullOrEmpty(Request["createnewaftersave"]))
            createNewAfterSave = Convert.ToBoolean(Request["createnewaftersave"]);

        //сохранение из режима редактирования
        if (!create && !String.IsNullOrEmpty(Request["editmode"]))
        {
            lockOrderInfo = GetLockOrderInfo(entity.Id.ToString());
            if (lockOrderInfo != null && lockOrderInfo.userId == (Session["user"] as User).Id)
            {
                shouldUnlockOrder = Convert.ToBoolean(Request["editmode"]);
                userCanEditOrder = true;
            }
            else
            {
                ViewData["userCanEditOrder"] = false;
                throw (new InvalidOperationException("Операция сохранения не выполнена. Заявка не заблокирована от имени пользователя, который пытается сохранить заявку."));
            }
        }

        base.BeforeSubmit(dc, entity);
    }

    protected override void AfterSubmit(NotarialServiceDataContext dc, Order entity)
    {
        base.AfterSubmit(dc, entity);

        CheckNewDocTemplates(entity);

        DeleteFormNumbers(dc, entity);
        SaveFormNumbers(dc, entity);

        //резлочим заявку после сохранения в режиме редактирования
        if (shouldUnlockOrder)
            if (!(statusIdChanged && entity.Status.Name.IndexOf("Принято в работу") > -1))
            {
                UnlockOrder(entity.Id, (Session["user"] as IUser).Id);
                userCanEditOrder = false;
            }
            else
                editMode = true;

        if (create)
            ViewData["ordercreated"] = entity.Id;

        if (createNewAfterSave)
            RedirectToAction("create", new string[] { entity.Id.ToString() });
        else
        {
            ViewData["message"] = "Сохранено успешно";
            RedirectToAction("edit", new string[] { entity.Id.ToString() });
        }

        Logger.WriteLog(dc, (create) ? LogOperationType.OrderCreate : LogOperationType.OrderChange, (Session["User"] as User).Id, entity.Id, entity.StatusId);
        if (statusIdChanged && !create)
            Logger.WriteLog(dc, LogOperationType.OrderStatusChange, (Session["User"] as User).Id, entity.Id, entity.StatusId);
        if (isPaidChanged && entity.IsPaid)
            Logger.WriteLog(dc, LogOperationType.OrderPaid, (Session["User"] as User).Id, entity.Id, entity.StatusId);
    }

    public override void View(string entityid)
    {
        var user = (Session["user"] as IUser);

        lockOrderInfo = GetLockOrderInfo(entityid);
        if (lockOrderInfo != null && lockOrderInfo.userId == user.Id)
            editMode = true;

        //если передан ключ печати, то определим тип печатного документа и подготовим для него данные
        if (!String.IsNullOrEmpty(Request["print"]) && !String.IsNullOrEmpty(Request["type"]))
        {
            int typeId = 0;
            if (Int32.TryParse(Request["type"], out typeId) && PrepareForPrintMode(entityid, typeId))
                printMode = true;
            else
                ViewData.Exceptions.Add(new ArgumentException("Некорректно задан тип печатного документа или недостаточно полномочий. Операция печати отклонена."));


            if (typeId == 2) //печать квитанции путем замены макросов в doc документе
            {
                try
                {
                    using (var dc = new NotarialServiceDataContext())
                    {
                        var order = dc.Orders.FirstOrDefault(o => o.Id == Convert.ToInt32(entityid));
                        string reportFileName = WordReport.GetReportFileName(order, GetOrderClients(dc, order.Id), GetOrderResponsibles(dc, order.Id), Server.MapPath("reports"), ConfigurationManager.AppSettings["wordTemplateName"]);
                        using (FileStream fs = File.OpenRead(Path.Combine(Server.MapPath("reports/temp"), reportFileName)))
                        {
                            byte[] b = new byte[fs.Length];
                            fs.Read(b, 0, (int)fs.Length);
                            Response.ContentType = "application/octet-stream";
                            Response.AppendHeader("Content-Disposition", "attachment;filename=" + reportFileName.Replace(' ', '_'));
                            Response.HeaderEncoding = System.Text.Encoding.GetEncoding("windows-1251");
                            Response.BinaryWrite(b);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Response.Write("Не удалось создать отчет. " + ex.Message + "\n" + ex.StackTrace);
                }
                return;
            }
        }

        if (!String.IsNullOrEmpty(Request["popup"]))
        {
            ViewData["popup"] = true;
            LayoutPath = "~/views/layouts/popup.haml";
        }

        base.View(entityid);
    }

    private DateTime GetOrderFinalDate(string entityid)
    {
        var result = new DateTime();

        int id;
        if (!String.IsNullOrEmpty(entityid) && Int32.TryParse(entityid, out id) && id > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                result = (from oh in dc.OrderHistories
                          where oh.Status.ActionName == "К выдаче"
                            && oh.OrderId == id
                          orderby oh.Date descending
                          select oh.Date).FirstOrDefault();
            }
        }
        return result;
    }

    private bool PrepareForPrintMode(string entityid, int type)
    {
        var user = Session["user"] as User;
        if (type > 0)
        {
            switch (type)
            {
                case 1:
                    TemplatePath = "~/views/order/printform.haml";
                    break;
                case 2:
                    if (!user.HasPermission("DocumentRecieve")) return false;
                    TemplatePath = "~/views/order/printreceipt.haml";
                    ViewData["isA5format"] = true;
                    break;
                case 3:
                    if (!user.HasPermission("PrintInvoice")) return false;
                    TemplatePath = "~/views/order/printpayment.haml";
                    ViewData["finaldate"] = GetOrderFinalDate(entityid);
                    break;
                default: return false;
            }
            LayoutPath = "~/views/layouts/printpage.haml";
            RegisterScript("order/print.js");
            return true;
        }
        else
            return false;
    }

    public void Edit(string entityid)
    {
        int entityId;
        var user = Session["user"] as User;
        if (Int32.TryParse(entityid, out entityId) && entityId > 0 && user != null)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var entity = dc.Orders.FirstOrDefault(o => o.Id == entityId);
                if (entity == null)
                {
                    ViewData.Exceptions.Add(new NullReferenceException("Переход в режим редактирования запрещен. Заявка не существует или была удалена."));
                    List();
                    return;
                }
                userCanEditOrder = user.CanEditOrder(dc, entity);
            }
            if (userCanEditOrder)
            {
                lockOrderInfo = LockOrder(entityid);

                if (lockOrderInfo != null && lockOrderInfo.userId == user.Id)
                    editMode = true;
                else
                    ViewData.Exceptions.Add(new Exception("Переход в режим редактирования запрещен. Заявка уже заблокирована другим пользователем."));
            }
            else
                ViewData.Exceptions.Add(new Exception("Переход в режим редактирования запрещен. У Вас недостаточно прав для редактирвоания выбранной заявки."));

        }
        base.View(entityid);
    }

    public override void List()
    {
        if (!String.IsNullOrEmpty(Request["tb_statusid"]))
        {
            int statusId;
            Int32.TryParse(Request["tb_statusid"], out statusId);

            if (statusId > -1)
                _StatusId = statusId;
        }

        //если это НЕ ajax-request
        if (String.IsNullOrEmpty(Request["nolayout"]))
        {
            RegisterScript("order/list.js");
            RegisterStylesheet("order/order.css");
            int interval = 0;
            var intervalStr = ConfigurationManager.AppSettings["orderlistrefreshperiodseconds"];
            if (!String.IsNullOrEmpty(intervalStr))
                Int32.TryParse(intervalStr, out interval);
            ViewData["refreshInterval"] = (interval > 0) ? interval : _DefaultRefreshInterval;
        }
        RegisterScript("jquery.blockUI.js");
        base.List();
    }

    private void LoadStatusesInfo(NotarialServiceDataContext dc)
    {
        var statuses = (from s in dc.Status orderby s.SortOrder select new StatusInfo(s.Id, s.Name, s.Orders.Count)).ToList();

        //if (statuses.Count > 1)
        //statuses.RemoveAt(statuses.Count - 1);


        statuses.Add(new StatusInfo(0, "Всего", dc.Orders.Count()));

        ViewData["statuses"] = statuses;
        ViewData["statusid"] = _StatusId;
    }

    protected override void CustomInit(NotarialServiceDataContext dc)
    {
        var statuses = (from s in dc.Status orderby s.SortOrder select new StatusInfo(s.Id, s.Name, s.Orders.Count)).ToList();

        //if (statuses.Count > 1)
        //statuses.RemoveAt(statuses.Count - 1);

        statuses.Add(new StatusInfo(0, "Всего", dc.Orders.Count()));

        ViewData["statuses"] = statuses;
        ViewData["statusid"] = _StatusId;
        base.CustomInit(dc);
    }

    public void GetStatusesInfo()
    {
        using (var dc = new NotarialServiceDataContext())
        {
            LoadStatusesInfo(dc);
        }
        LayoutPath = "none";
        TemplatePath = "~/views/order/_statuslist.haml";
        RenderView();
    }

    protected override void RowAdded(NotarialServiceDataContext dc, object _entity, ref System.Data.DataRow dr)
    {
        var entity = (OrderDetails)_entity;
        var dt = dr.Table;

        var lockedOrders = GetLockedOrdersInfo();

        var info = lockedOrders.FirstOrDefault(o => o.id == entity.Id);

        if (info != null)
        {
            if (dt.Columns.Contains("LockDate"))
                dr["LockDate"] = info.date;
            if (dt.Columns.Contains("LockUserId"))
                dr["LockUserId"] = info.userId.ToString();
            if (dt.Columns.Contains("LockUserName"))
                dr["LockUserName"] = info.userName;
        }

        if (dr.Table.Columns.Contains("Deadline") && !ViewData.ContainsKey("excel") && entity.IsRush)
            dr["Deadline"] += String.Format(@"<img src=""img/warning.png"" title=""{0}"" alt=""{0}"" class=""indent5 icon16"" />", "Срочная заявка");
    }

    protected override IQueryable ConvertQueryToResult(NotarialServiceDataContext dc, IQueryable<Order> query)
    {
        return (from q in query
                select new OrderDetails
                {
                    Id = q.Id,
                    TaskId = q.TaskId,
                    Description = q.Description,
                    StatusIcon = q.Status.Image16,
                    StatusId = q.StatusId,
                    StatusName = q.Status.Name,
                    NotarialActionName = q.NotarialAction.Name,
                    NotarialActionFullName = q.NotarialAction.FullName,
                    NotarialActionId = q.NotarialActionId,
                    Deadline = q.Deadline,
                    NotarialRegisterId = q.NotarialRegisterId,
                    NotarialRegisterName = q.NotarialRegister.Name,
                    NotarialRegisterRow = q.NotarialRegisterRow,
                    NotarialRegisterInfo = String.Format("{0}{1}", ((q.NotarialRegister != null) ? q.NotarialRegister.Name + " / " : ""), q.NotarialRegisterRow),

                    //additional rows
                    IsComplicated = q.IsComplicated,
                    IsInternal = q.IsInternal,
                    CanBeAssign = q.CanBeAssign,
                    IsNotForRegistration = q.IsNotForRegistration,
                    IsRush = q.IsRush,

                    CopyCount = q.CopyCount,
                    DocCount = q.DocCount,
                    XeroxCount = q.XeroxCount,

                    IsNotOnForm = q.IsNotOnForm,
                    FormNumber = //(q.Status.Name == "Выдано") закомментировал ибо падает
                         String.Join(", ",                                 
                            (
                                    from nf in dc.NotarialFormNumber
                                    where nf.OrderId == q.Id
                                    select nf.Name
                            ).ToArray<string>()),

                    RowClass = ((q.Deadline < DateTime.Now && q.Status.Name != "Выдано") ? " expired " : "") + ((q.IsRush && q.Status.Name != "Выдано") ? " rush " : ""),

                    //cost rows
                    IsPrepayment = q.IsPrepayment,
                    IsCashless = q.IsCashless,
                    IsPaid = q.IsPaid,
                    StatusEditorId = q.StatusEditorId,
                    StatusEditorName = q.StatusEditorId > 0 ? (q.StatusEditor.LastName + " " + q.StatusEditor.Name[0] + ". " + q.StatusEditor.PatronymicName[0] + ".") : "",

                    //довольно "корявое" решение c формированием ссылок на клиентов, хорошо бы в будущем переделать
                    Clients = String.Join("<br/>",
                                (
                                    from op in dc.OrderClientPersons
                                    where op.OrderId == q.Id
                                    select String.Format("<a href=\"#\" onclick=\" openDialog('person.ivp/view/{0}?popup=true&edit=true'); return false;\">{1}</a>", op.Person.Id, op.Person.LastName + " " + (op.Person.Name != null && string.Empty != op.Person.Name.Trim() ? op.Person.Name[0] + "." : "") + (op.Person.PatronymicName != null && string.Empty != op.Person.PatronymicName.Trim() ? op.Person.PatronymicName[0] + "." : ""))
                                ).Union
                                (
                                    from oc in dc.OrderClientCompanies
                                    where oc.OrderId == q.Id
                                    select String.Format("<a href=\"#\" onclick=\" openDialog('company.ivp/view/{0}?popup=true&edit=true'); return false;\">{1}</a>", oc.Company.Id, oc.Company.Name)
                                )
                                .ToArray<string>()),
                    Responsibles = String.Join("<br/>",
                                (
                                    from op in dc.OrderResponsiblePersons
                                    where op.OrderId == q.Id
                                    select String.Format("<a href=\"#\" onclick=\" openDialog('person.ivp/view/{0}?popup=true&edit=true'); return false;\">{1}</a>", op.Person.Id, op.Person.LastName + " " + op.Person.Name[0] + "." + op.Person.PatronymicName[0] + ".")
                                ).Union
                                (
                                    from oc in dc.OrderResponsibleCompanies
                                    where oc.OrderId == q.Id
                                    select String.Format("<a href=\"#\" onclick=\" openDialog('company.ivp/view/{0}?popup=true&edit=true'); return false;\">{1}</a>", oc.Company.Id, oc.Company.Name)
                                )
                                .ToArray<string>())
                });
    }

    protected override IQueryable<Order> CustomFilter(NotarialServiceDataContext dc, IQueryable<Order> query)
    {
        if (!string.IsNullOrEmpty(State["tb_personid"]) && Int32.TryParse(State["tb_personid"], out _PersonId) && _PersonId > 0)
            return query.Where(o => (o.OrderClientPersons.Any(ocp => ocp.PersonId == _PersonId) || (o.OrderResponsiblePersons.Any(orp => orp.PersonId == _PersonId))));
        else if (!string.IsNullOrEmpty(State["tb_companyid"]) && Int32.TryParse(State["tb_companyid"], out _CompanyId) && _CompanyId > 0)
            return query.Where(o => (o.OrderClientCompanies.Any(ocp => ocp.CompanyId == _CompanyId) || (o.OrderResponsibleCompanies.Any(orp => orp.CompanyId == _CompanyId))));
        else if (_StatusId > -1)
            return query.Where(o => o.StatusId == _StatusId || _StatusId == 0);
        else
            return query;
    }

    [AllowedRole("DocumentRecieve")]
    public void Create(string orderid)
    {
        Int32.TryParse(orderid, out parentOrderId);
        ViewData["ordercreated"] = parentOrderId;
        Create();
    }

    [AllowedRole("DocumentRecieve")]
    public override void Create()
    {
        base.Create();
    }

    protected override void BeforeEntityInitialized(NotarialServiceDataContext dc, Order entity)
    {
        entity.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(OrderPropertyChanged);
    }

    void OrderPropertyChanged(Object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName.ToUpperInvariant().Trim() == "ISPAID")
            isPaidChanged = true;
        if (e.PropertyName.ToUpperInvariant().Trim() == "STATUSID")
            statusIdChanged = true;
        if (e.PropertyName.ToUpperInvariant().Trim() == "NOTARIALACTIONID")
            notarialActionChanged = true;
    }
    /*
    private void CopyNotarialDocTemplates(NotarialServiceDataContext dc, Order entity)
    {
        var nfiles = entity.NotarialAction.NotarialActionFiles.Where(f => !f.IsDeleted).ToList();
        var user = Session["User"] as User;

        if (nfiles.Count > 0)
        {
            foreach (var nf in nfiles)
            {
                var file = new OrderDocTemplate { Created = DateTime.Now, CreatorId = user.Id, Data = nf.Data, OrderId = entity.Id, Name = nf.Name };
                //file.Name = GenerateOrderDocTemplateName(entity, nf.Name, user);
                dc.OrderDocTemplates.InsertOnSubmit(file);
            }

            dc.SubmitChanges();
        }
    }*/

    private static string GetFileExtension(string fileName)
    {
        var result = string.Empty;
        var index = fileName.LastIndexOf('.');
        if (index > 0)
            result = fileName.Substring(index);
        return result;
    }

    private static string GetFileNameWithoutExtension(string fileName)
    {
        var result = fileName;
        var index = fileName.LastIndexOf('.');
        if (index > 0)
            result = fileName.Substring(0, index);
        return result;
    }

    private string GetOperation()
    {
        var sb = new StringBuilder();

        if (create)
        {
            sb.Append("Создание");
            if (isPaidChanged)
                sb.Append(" и оплата");
        }
        else
        {
            if (orderChanged)
            {
                sb.Append("Изменение");
                if (isPaidChanged)
                    sb.Append(" и оплата");
            }
            else if (isPaidChanged)
                sb.Append("Олпата");
        }
        return sb.ToString();
    }

    private void SaveClients(NotarialServiceDataContext dc, Order order)
    {
        var idStr = Request["clientids"];

        int[] personIds, companyIds;
        GetClientsIdsFromString(idStr, out personIds, out companyIds);

        order.OrderClientPersons.UpdateEntitySet("PersonId", personIds, dc);
        order.OrderClientCompanies.UpdateEntitySet("CompanyId", companyIds, dc);
    }

    private void SaveResponsibles(NotarialServiceDataContext dc, Order order)
    {
        var idStr = Request["responsibleids"];

        int[] personIds, companyIds;
        GetClientsIdsFromString(idStr, out personIds, out companyIds);

        order.OrderResponsiblePersons.UpdateEntitySet("PersonId", personIds, dc);
        order.OrderResponsibleCompanies.UpdateEntitySet("CompanyId", companyIds, dc);
    }

    private void DeleteFormNumbers(NotarialServiceDataContext dc, Order order)
    {
        var idStr = RequestHelper.GetIdsFromString(Request["nformsdeleted"]);
        var user = Session["user"] as IUser;
        var message = Request["forminvalidmessage"];
        foreach (var id in idStr)
        {
            var form = order.NotarialForm.FirstOrDefault(f => f.Id == id);
            if (form != null)
            {
                form.OrderId = order.Id;
                form.StatusId = (int)NotarialForm.NotarialFormStatusType.Defect;
                form.UserId = user.Id;
                form.Comment = !string.IsNullOrEmpty(message) 
                                    ? message 
                                    : ((order.IsNotOnForm) ? "Документ напечатан не на бланке" : string.Empty);
            }
        }
        dc.SubmitChanges();
    }

    private void SaveFormNumbers(NotarialServiceDataContext dc, Order order)
    {
        if (order.IsNotOnForm) return;

        var idArr = RequestHelper.GetIdsFromString(Request["formnumberids"]);
        var result = new List<NotarialForm>();
        var user = Session["user"] as IUser;
        var isModal = !string.IsNullOrEmpty(Request["ismodal"]);

        foreach (var id in idArr)
        {
            var reqIdStr = string.Format("fnbr{0}", id);
            var numberStr = isModal ? id.ToString() : Request[reqIdStr];
            if (string.IsNullOrEmpty(numberStr)) continue;
            long number = 0;
            if (long.TryParse(numberStr, out number) && number > 0)
            {
                var form = dc.NotarialForm.FirstOrDefault(f => f.Name == number
                                                               && !f.OrderId.HasValue
                                                               && f.StatusId == (int)NotarialForm.NotarialFormStatusType.NotUsed);
                if (form != null)
                {
                    form.OrderId = order.Id;
                    form.StatusId = (int)NotarialForm.NotarialFormStatusType.Used;
                    form.UserId = user.Id;
                    if (order.Status.Name.IndexOf("Выдано") > -1)
                        form.EndDate = DateTime.Now;
                    result.Add(form);
                }
                //else
                    //throw new Exception(string.Format("Ошибка сохранения бланка. Бланк с номером 77AA{0} не найден ни в одной пачке или уже использован/испорчен", number.AddLeadingZero(7)));
            }
            //else
               // throw new Exception(string.Format("Ошибка сохранения бланка. Номер бланка {0} указан в неверном формате.", numberStr));
        }

        if (result.Count > 0)
            order.NotarialForm.AddRange(result);

        dc.SubmitChanges();

        return;
    }

    private void GetClientsIdsFromString(string idStr, out int[] personIds, out int[] companyIds)
    {
        personIds = new int[0];
        companyIds = new int[0];

        if (String.IsNullOrEmpty(idStr)) return;

        int[] idArr = RequestHelper.GetIdsFromString(idStr);

        List<int> pList = new List<int>(idArr.Length);
        List<int> cList = new List<int>(idArr.Length);

        foreach (int id in idArr)
        {
            if (Client.CheckIsNatural(id))
                pList.Add(Client.GetIdByCid(id));
            else
                cList.Add(Client.GetIdByCid(id));
        }

        if (pList.Count > 0)
            personIds = pList.ToArray();
        if (cList.Count > 0)
            companyIds = cList.ToArray();
    }

    public void FindClients()
    {
        if (string.IsNullOrEmpty(Request["q"]) || Request["q"].Length < 5) return;
        string search = string.Format("%{0}%", Request["q"].Replace(' ', '%'));

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var clients = (from c in dc.Clients
                           where !c.IsArchive && SqlMethods.Like(c.Name, search)
                           orderby c.Name
                           select new { c.Cid, c.Name, c.SearchInfo, c.IsCashless, c.IsNatural, c.IsNotify, c.NotifyComment });
            foreach (var client in clients)
                sb.AppendLine(String.Format("{0}|{1}|{2}|{3}|{4}|{5}"
                    , (!client.IsNatural && client.SearchInfo.Length > 30)
                        ? client.SearchInfo.Substring(0, 30) + "...)"
                        : client.SearchInfo
                    , client.Cid
                    , ((client.IsCashless) ? "True" : "False")
                    , client.Name
                    , ((client.IsNotify) ? "True" : "False")
                    , client.NotifyComment
                ));
            Response.Write(sb.ToString());
        }
    }

    public void FindNotarialActions()
    {
        if (string.IsNullOrEmpty(Request["q"])) return;
        string search = string.Format("%{0}%", Request["q"].Replace(' ', '%'));

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var actions = (from n in dc.NotarialActions
                           where !n.IsArchive && SqlMethods.Like(n.FullName, search)
                           orderby n.Path
                           select new { n.Id, n.FullName, n.IsComplicated });
            foreach (var a in actions)
            {
                sb.AppendLine(String.Format("{0}|{1}|{2}", a.FullName, a.Id, a.IsComplicated ? "True" : ""));
            }
            Response.Write(sb.ToString());
        }
    }

    public void FindNotarialFormNumber()
    {
        var q = Request["q"];
        var isAll = !string.IsNullOrEmpty(Request["all"]);
        int value;
        if (string.IsNullOrEmpty(q) || q.Length < 6 || !int.TryParse(q, out value) || value < 0) return;

        string search = string.Format("%{0}%", value);

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var actions = (from nf in dc.NotarialForm
                           where (isAll || nf.StatusId == (int)NotarialForm.NotarialFormStatusType.NotUsed)
                            && SqlMethods.Like(nf.Search, search)
                           orderby nf.Name
                           select new { nf.Id, nf.FullName });
            foreach (var a in actions)
            {
                sb.AppendLine(String.Format("{0}|{1}|{2}", a.FullName,  a.Id, string.Format("77AA{0}",a.FullName)));
            }
            Response.Write(sb.ToString());
        }
    }

    public void CheckTask(string taskid)
    {
        int id;
        Int32.TryParse(taskid, out id);

        if (id > -1)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var task = dc.Tasks.FirstOrDefault(t => t.Id == id);
                if (task != null)
                {
                    var order = dc.Orders.FirstOrDefault(o => o.TaskId == id);
                    var sb = new StringBuilder();
                    if (order != null)
                        sb.Append("По записи на прием уже создана заявка. Создать еще одну ?");

                    int orderOverdueMinutes;

                    if (!Int32.TryParse(ConfigurationManager.AppSettings["orderoverdueminutes"], out orderOverdueMinutes))
                        orderOverdueMinutes = 30;

                    if (task.StartDate.AddMinutes(orderOverdueMinutes) < DateTime.Now)
                    {
                        sb.Append(Environment.NewLine);
                        sb.Append(String.Format("Запись на прием просрочена более чем на {0} минут. Продолжить?", orderOverdueMinutes));
                    }

                    var r = new AJAXResult
                    {
                        Description = sb.ToString(),
                        IsOK = true
                    };
                    RenderJSON(r);
                }
                else
                {
                    RenderJSON(new AJAXResult(new Exception("Заявки с таким номером не существует")));
                    return;
                }
            }
        }
        else
        {
            RenderJSON(new AJAXResult(new Exception("Номер заявки задан некорректно")));
            return;
        }
    }

    public void LoadTaskData(string taskid)
    {
        int id;
        Int32.TryParse(taskid, out id);

        if (id > -1)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var task = dc.Tasks.FirstOrDefault(t => t.Id == id);
                if (task != null)
                {
                    var sb = new StringBuilder();

                    sb.AppendFormat("{0}{1}", task.NotarialActionId, delimeter);
                    sb.AppendFormat("{0}{1}", task.NotarialAction.Name, delimeter);
                    sb.AppendFormat("{0}{1}", task.NotarialAction.IsComplicated, delimeter);
                    sb.AppendFormat("{0}{1}", task.Comment, delimeter);
                    sb.AppendFormat("{0},{1}{2}", task.Person.Cid.ToString(), task.Person.FullName, delimeter);
                    sb.AppendFormat("{0},{1}", task.Person.IsNotify ? "True" : "False", task.Person.NotifyComment);

                    var r = new AJAXResult
                    {
                        Result = sb.ToString(),
                        IsOK = true
                    };
                    RenderJSON(r);
                }
                else
                {
                    RenderJSON(new AJAXResult(new Exception("Заявки с таким номером не существует")));
                    return;
                }
            }
        }
        else
        {
            RenderJSON(new AJAXResult(new Exception("Номер заявки задан некорректно")));
            return;
        }
    }

    private LockOrderInfo LockOrder(string entityid)
    {
        LockOrderInfo orderInfo = null;
        int id;
        Int32.TryParse(entityid, out id);

        var user = Session["user"] as IUser;

        if (id > -1 && user != null)
        {
            var app = Context.Application;
            lock (lockObj)
            {
                var lockedOrders = app.Get("lockedOrders") as List<LockOrderInfo>;

                if (lockedOrders == null)
                    lockedOrders = new List<LockOrderInfo>();

                orderInfo = lockedOrders.FirstOrDefault<LockOrderInfo>(o => o.id == id);
                //если заявка не залочена - залочим ее сами
                if (orderInfo == null)
                {
                    orderInfo = new LockOrderInfo(id, DateTime.Now, user.Id, user.Name);
                    lockedOrders.Add(orderInfo);
                    //если теперь заявка залочена нами - разлочим все остальные наши заявки
                    lockedOrders.RemoveAll(o => o.userId == user.Id && o.id != id);
                    app.Lock();
                    try
                    {
                        app.Set("lockedOrders", lockedOrders);
                    }
                    catch { }
                    finally { app.UnLock(); }
                }
            }
        }
        return orderInfo;
    }

    private LockOrderInfo GetLockOrderInfo(int entityId)
    {
        LockOrderInfo orderInfo = null;
        var user = Session["user"] as IUser;

        if (entityId > -1 && user != null)
        {
            var lockedOrders = Context.Application.Get("lockedOrders") as List<LockOrderInfo>;

            if (lockedOrders != null)
                orderInfo = lockedOrders.FirstOrDefault(o => o.id == entityId);
        }

        return orderInfo;
    }

    private LockOrderInfo GetLockOrderInfo(string entityid)
    {
        int id;
        Int32.TryParse(entityid, out id);
        return GetLockOrderInfo(id);
    }

    private List<LockOrderInfo> GetLockedOrdersInfo()
    {
        if (lockedOrdersInfo == null)
            lockedOrdersInfo = Context.Application.Get("lockedOrders") as List<LockOrderInfo>;
        return lockedOrdersInfo ?? new List<LockOrderInfo>(0);
    }

    private IList<NotarialForm> GetNotarialForms(NotarialServiceDataContext dc, Order entity)
    {
        return dc.NotarialForm.Where(f => f.OrderId == entity.Id
                                            && f.StatusId == (int)NotarialForm.NotarialFormStatusType.Used)
                              .ToList();
    }

    private List<IClient> GetOrderResponsibles(NotarialServiceDataContext dc, int orderId)
    {
        List<IClient> responsibles = new List<IClient>();
        responsibles.AddRange((from rc in dc.OrderResponsibleCompanies where rc.OrderId == orderId || (parentOrderId > 0 && rc.OrderId == parentOrderId) || (clonedOrderId > 0 && rc.OrderId == clonedOrderId) select (IClient)rc.Company).ToList<IClient>());
        responsibles.AddRange((from rp in dc.OrderResponsiblePersons where rp.OrderId == orderId || (parentOrderId > 0 && rp.OrderId == parentOrderId) || (clonedOrderId > 0 && rp.OrderId == clonedOrderId) select (IClient)rp.Person).ToList<IClient>());
        return responsibles;
    }

    private List<IClient> GetOrderClients(NotarialServiceDataContext dc, int orderId)
    {
        List<IClient> clients = new List<IClient>();
        clients.AddRange((from cp in dc.OrderClientPersons where cp.OrderId == orderId || (parentOrderId > 0 && cp.OrderId == parentOrderId) || (clonedOrderId > 0 && cp.OrderId == clonedOrderId) select (IClient)cp.Person).ToList<IClient>());
        clients.AddRange((from cc in dc.OrderClientCompanies where cc.OrderId == orderId || (parentOrderId > 0 && cc.OrderId == parentOrderId) || (clonedOrderId > 0 && cc.OrderId == clonedOrderId) select (IClient)cc.Company).ToList<IClient>());
        return clients;
    }

    #region Разблокировка заявки
    public void UnlockOrder(int orderid, int userid)
    {
        var user = Session["user"] as IUser;

        if (orderid > -1 && user != null && user.Id == userid)
        {
            var app = Context.Application;
            lock (lockObj)
            {
                var lockedOrders = app.Get("lockedOrders") as List<LockOrderInfo>;

                if (lockedOrders == null)
                    lockedOrders = new List<LockOrderInfo>();

                lockedOrders.RemoveAll(o => (o.userId == user.Id || user.HasPermission("UnlockOrder")) && o.id == orderid);
                app.Lock();
                try
                {
                    app.Set("lockedOrders", lockedOrders);
                }
                catch { }
                finally { app.UnLock(); }
            }
        }
    }

    public void UnlockOrder(string orderid, string userid)
    {
        int userId, orderId;

        if (Int32.TryParse(orderid, out orderId) && Int32.TryParse(userid, out userId))
            UnlockOrder(orderId, userId);
    }

    public void UnlockOrder()
    {
        try
        {
            UnlockOrder(Request["orderid"], Request["userid"]);
        }
        catch (Exception ex)
        {
            if (!String.IsNullOrEmpty(Request["json"]))
                RenderJSON(new AJAXResult(ex));
            else
                throw (ex);
        }

        if (!String.IsNullOrEmpty(Request["json"]))
            RenderJSON(new AJAXResult(true));
    }
    #endregion

    #region Изменение статуса
    private bool ChangeStatus(int orderId, int statusId, int oldStatusId, string comment)
    {
        if (GetLockOrderInfo(orderId.ToString()) == null)
            using (var dc = new NotarialServiceDataContext())
            {
                var order = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                if (order != null)
                {
                    if (order.StatusId == oldStatusId)
                    {
                        order.StatusId = statusId;
                        order.Operation = "Изменение";
                        if (!String.IsNullOrEmpty(comment))
                            order.Comment = comment;
                        order.EditorId = (Session["user"] as User).Id;

                        bool result = dc.Status.Any(s => s.Id == statusId && s.Name.IndexOf("Принято в работу") > -1);

                        Logger.WriteLog(dc, LogOperationType.OrderStatusChange, (Session["user"] as User).Id, orderId, statusId, false);

                        dc.SubmitChanges();

                        return result;
                    }
                    else
                        throw (new InvalidOperationException("Операция изменения статуса не выполнена. Статус был изменен другим пользователем."));

                }
                else
                    throw (new NullReferenceException("Операция изменения статуса не выполнена. Заявка не существует или была удалена."));
            }
        else
            throw (new InvalidOperationException("Операция изменения статуса не выполнена. Заявка заблокирована другим пользователем."));
    }

    public void ChangeStatus()
    {
        int orderId = 0, statusId = 0, oldStatusId = 0;
        string comment = Request["comment"];

        if (Int32.TryParse(Request["orderid"], out orderId) && orderId > 0
            && Int32.TryParse(Request["statusid"], out statusId) && statusId > 0
            && Int32.TryParse(Request["oldstatusid"], out oldStatusId) && oldStatusId > -1)
        {
            try
            {
                ChangeStatus(orderId, statusId, oldStatusId, comment);
            }
            catch (InvalidOperationException ex)
            {
                RenderJSON(new AJAXResult(ex));
                return;
            }
            catch (NullReferenceException ex)
            {
                RenderJSON(new AJAXResult(ex));
                return;
            }
            catch (Exception ex)
            {
                RenderJSON(new AJAXResult(ex));
                return;
            }
            RenderJSON(new AJAXResult(true));
        }
        else
        {
            RenderJSON(new ArgumentException("Аргументы заданы некорректно."));
            return;
        }
    }

    public void ChangeStatus(string orderid, string statusid, string oldstatusid)
    {
        ChangeStatus(orderid, statusid, oldstatusid, "");
    }

    public void ChangeStatus(string orderid, string statusid, string oldstatusid, string comment)
    {
        int orderId = 0, statusId = 0, oldStatusId = 0;
        bool editMode = false;
        bool returnAfterSave = !string.IsNullOrEmpty(Request["returnAfterSave"]);

        if (Int32.TryParse(orderid, out orderId) && orderId > 0
            && Int32.TryParse(statusid, out statusId) && statusId > 0
            && Int32.TryParse(oldstatusid, out oldStatusId) && oldStatusId > -1)
        {
            try
            {
                editMode = ChangeStatus(orderId, statusId, oldStatusId, comment);
            }
            catch (InvalidOperationException ex)
            {
                ViewData.Exceptions.Add(ex);
            }
            catch (NullReferenceException ex)
            {
                ViewData.Exceptions.Add(ex);
                List();
                return;
            }
            catch (Exception ex)
            {
                ViewData.Exceptions.Add(ex);
            }
        }
        if (returnAfterSave)
            ReturnAfterSave();
        else if (editMode)
            Edit(orderid);
        else
            View(orderid);
    }
    #endregion

    [AllowedRole("DeleteOrder", "UnlockOrder")]
    public override void Delete()
    {
        base.Delete();
    }

    protected override void EntityDeleted(NotarialServiceDataContext dc, List<Order> deleted)
    {
        base.EntityDeleted(dc, deleted);

        foreach (var entity in deleted)
            Logger.WriteLog(dc, LogOperationType.OrderDelete, (Session["User"] as User).Id, entity.Id, null);
    }

    [AllowedRole("MoneyRecieve")]
    public void ChangeIsPaidAttribute()
    {
        var isPaidStr = Request["ispaid"];
        var orderIdStr = Request["orderid"];

        if (!String.IsNullOrEmpty(isPaidStr) && !String.IsNullOrEmpty(orderIdStr))
        {
            var isPaid = Convert.ToBoolean(isPaidStr);
            var orderId = Convert.ToInt32(orderIdStr);
            string currentIsPaidValue = "";
            if (orderId > 0)
            {
                string result = "";
                var isOK = false;
                using (var dc = new NotarialServiceDataContext())
                {
                    try
                    {
                        var order = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                        if (order == null)
                            throw (new NullReferenceException("Операция изменения оплаты не выполнена. Заявка не существует или была удалена."));
                        currentIsPaidValue = order.IsPaid.ToString();
                        if (order.IsPaid == !isPaid)
                            throw (new InvalidOperationException(String.Format("Операция изменения оплаты не выполнена. Заявка уже находится в статусе \"{0}оплачено\"", (isPaid) ? "не " : "")));
                        order.IsPaid = !isPaid;
                        order.EditorId = (Session["user"] as User).Id;
                        order.Operation = "Оплата";
                        dc.SubmitChanges();
                        isOK = true;

                        if (order.IsPaid)
                            Logger.WriteLog(dc, LogOperationType.OrderPaid, (Session["user"] as User).Id, order.Id, order.StatusId);
                    }
                    catch (InvalidOperationException ex)
                    {
                        result = ex.Message;
                    }
                    catch (NullReferenceException ex)
                    {
                        result = ex.Message;
                    }
                    catch (Exception ex)
                    {
                        result = ex.Message;
                    }
                    var r = new AJAXResult
                    {
                        Description = result,
                        IsOK = isOK,
                        Result = currentIsPaidValue
                    };
                    RenderJSON(r);
                }
            }
        }
    }

    public void GetActionMenu()
    {
        int orderId;
        var user = Session["user"] as User;

        if (user != null && Int32.TryParse(Request["orderid"], out orderId) && orderId > 0)
        {
            var actions = new List<OrderAction>(12);

            using (var dc = new NotarialServiceDataContext())
            {
                var entity = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                if (entity.Id > 0 && entity != null)
                {
                    var lockOrderInfo = GetLockOrderInfo(entity.Id);
                    var userpermissions = (from up in dc.UserPermissions where up.UserId == user.Id select up.PermissionId).ToList();
                    var statuses = (from ws in dc.WorkflowStatusMatrixes
                                    where ws.FromStatusId == (int?)entity.StatusId
                                        && (!ws.PermissionId.HasValue || userpermissions.Contains(ws.PermissionId.Value))
                                    select ws.ToStatus).OrderBy(s => s.SortOrder).Distinct().ToList();

                    if (lockOrderInfo == null)
                        foreach (var s in statuses)
                        {
                            //для статуса выдано проверяем оплачена ли заявка
                            if (!entity.IsInternal && !entity.IsPaid && (s.Name.IndexOf("Выдано") > -1) /* && !user.HasPermission("MoneyRecieve") */) continue;

                            var actionStr = String.Format("executeAction('changestatus',{0}json: true, timestamp: new Date(), orderid: {2}, statusid: {3}, oldstatusid: {4}, comment: ( ($('#comment').length > 0) ? $('#comment').val() : '' ){1});", "{", "}", entity.Id, s.Id, entity.StatusId);

                            if (s.Name.IndexOf("Отказ") > -1)
                                actionStr = "if (!(checkStatus(this))) return false; " + actionStr + "return false;";
                            if (s.Name.IndexOf("Принято в работу") > -1)
                                actionStr = actionStr + String.Format(" editOrder({0}); return false;", entity.Id);

                            actions.Add(new OrderAction(s.ActionName, "#", actionStr + "return false;"));
                        }

                    if (lockOrderInfo == null || lockOrderInfo.userId == user.Id)
                        if (user.CanEditOrder(dc, entity))
                            actions.Add(new OrderAction("Изменить", String.Format("order.ivp/edit/{0}", entity.Id), ""));

                    if (!entity.IsInternal && !entity.IsPaid && user.HasPermission("MoneyRecieve"))
                        actions.Add(new OrderAction("Оплатить", "#", String.Format("return executeAction('changeispaidattribute',{0}json: 'true', timestamp: new Date(), ispaid: 'false', orderid: {2}{1})", "{", "}", entity.Id)));

                    //print actions
                    if (user.HasPermission("DocumentRecieve"))
                        actions.Add(new OrderAction("Печатать квитанцию", "#", String.Format("openDialog('order.ivp/view/{0}?print=true&type=2', 'Квитанция_по_заявке', {1} width: 860, height: 480  {2}); removeActionMenu(); return false;", orderId, "{", "}")));
                    if (user.HasPermission("PrintInvoice"))
                        actions.Add(new OrderAction("Печатать чек", "#", String.Format("openDialog('order.ivp/view/{0}?print=true&type=3', 'Подтверждения_взимания_платы', {1} width: 1060, height: 700 {2}); removeActionMenu(); return false;", orderId, "{", "}")));

                    actions.Add(new OrderAction("Печатать заявку", "#", String.Format("openDialog('order.ivp/view/{0}?print=true&type=1', 'Заявка_{0}', {1} width: 1060, height: 700 {2}); removeActionMenu(); return false;", orderId, "{", "}")));

                    if (lockOrderInfo != null && user.HasPermission("UnlockOrder"))
                        actions.Add(new OrderAction("Разблокировать", "#", String.Format("return executeAction('unlockorder',{0}json: true, timestamp: new Date(), orderid: {2}, userid: {3}{1})", "{", "}", entity.Id, user.Id)));

                    if (user.HasPermission("UnlockOrder") || user.HasPermission("DeleteOrder"))
                        actions.Add(new OrderAction("Удалить", "#", String.Format("if (!confirm('Вы уверены, что хотите удалить выбранную запись?')) return false; else return executeAction('delete',{0}json: true, timestamp: new Date(), id: {2}{1})", "{", "}", entity.Id)));

                    ViewData["entity"] = entity;
                }
            }
            LayoutPath = "none";
            TemplatePath = "~/views/order/actionmenu.haml";
            ViewData["actions"] = actions;

            RenderView();
        }
    }

    public void AddComment()
    {
        var comment = Request["comment"];
        var orderIdStr = Request["orderid"];
        int orderId;

        if (!String.IsNullOrEmpty(comment) && Int32.TryParse(orderIdStr, out orderId) && orderId > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                try
                {
                    var order = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                    if (order != null)
                    {
                        order.Comment = comment;
                        order.Operation = "Изменение";
                        dc.SubmitChanges();
                    }
                }
                catch (Exception ex)
                {
                    if (!String.IsNullOrEmpty(Request["json"]))
                        RenderJSON(new AJAXResult(ex));
                    else
                        throw (ex);
                }
            }
            if (!String.IsNullOrEmpty(Request["json"]))
                RenderJSON(new AJAXResult(true));
        }
        else
            RenderJSON(new AJAXResult(new Exception("Номер заявки некорректен или комментарий не задан.")));
    }

    [AllowedRole("EstimateOrders")]
    public void SaveMark()
    {
        var markIdStr = Request["mark"];
        var orderIdStr = Request["orderid"];
        int orderId, markId;

        if (!String.IsNullOrEmpty(markIdStr) && !String.IsNullOrEmpty(orderIdStr)
            && Int32.TryParse(markIdStr, out markId) && markId > 0
            && Int32.TryParse(orderIdStr, out orderId) && orderId > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                try
                {
                    var order = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                    if (order != null)
                    {
                        order.MarkId = markId;
                        order.Operation = "Изменение";
                        dc.SubmitChanges();
                    }
                }
                catch (Exception ex)
                {
                    if (!String.IsNullOrEmpty(Request["json"]))
                        RenderJSON(new AJAXResult(ex));
                    else
                        throw (ex);
                }
            }
            if (!String.IsNullOrEmpty(Request["json"]))
                RenderJSON(new AJAXResult(true));
        }
        else
            RenderJSON(new AJAXResult(new Exception("Номер заявки или оценка не заданы или некорректны.")));
    }

    public void GetTaskHistory()
    {
        var orderIdStr = Request["orderid"];
        int orderId;
        if (!String.IsNullOrEmpty(orderIdStr) && Int32.TryParse(orderIdStr, out orderId) && orderId > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var entity = dc.Orders.FirstOrDefault(o => o.Id == orderId);
                if (entity != null)
                {
                    ViewData["history"] = (from oh in dc.OrderHistories where oh.OrderId == entity.Id orderby oh.Date descending select oh).ToList();
                    LayoutPath = "none";
                    TemplatePath = "~/views/order/_history.haml";
                    RenderView();
                }
            }
        }
    }

    #region Создать колонки и условия фильтра
    protected override void CreateFilterConditions(NotarialServiceDataContext dc)
    {
        base.CreateFilterConditions(dc);
        var user = ViewData["user"] as User;
        Filter = new Filter<Order>();
        var fc = Filter.Conditions;

        fc.Add(new NumericFilterCondition<Order>("id", "Номер"));
        fc.Add(new SelectFilterCondition<Order>("statusid", "Статус", (from s in dc.Status orderby s.SortOrder select s)));
        fc.Add(new TreeFilterCondition<Order>("NotarialAction.Path", "Тип документа", (from c in dc.NotarialActions orderby c.FullName select new { Path = c.Path, FullName = c.FullName })));
        fc.Add(new SelectFilterCondition<Order>("notarialregisterid", "Номер реестра", (from n in dc.NotarialRegisters orderby n.Id select n)));
        fc.Add(new NumericFilterCondition<Order>("notarialregisterrow", "Номер записи реестра"));
        fc.Add(new StringFilterCondition<Order>("formnumber", "Номер бланка"));

        //ToDo фильтры по заявителям и поверенным
        //fc.Add(new RelationFilterCondition<Order>("OrderClientPersons", "PersonId", "Заявители", (from c in dc.Clients where c.IsNatural orderby c.Name select c)));
        //fc.Add(new RelationFilterCondition<Order>("OrderClientCompanies", "CompanyId", "Заявители", (from c in dc.Clients where !c.IsNatural orderby c.Name select c)));
        //fc.Add(new UserRelationFilterCondition<Order>("OrderClientCompanies", "CompanyId", "Заявители", (from c in dc.Clients orderby c.Name select c), user.Id, 
        //ToDo фильтры по LockDate и UserDate

        fc.Add(new UserRelationFilterCondition<Order>("OrderClients", "ClientCid", "Заявители", (from c in dc.Clients orderby c.Name select new { Id = c.Cid, Name = c.Name }), -1, "order.ivp/findclients", "client.ivp/select?reset=true"));
        fc.Add(new UserRelationFilterCondition<Order>("OrderResponsibles", "ResponsibleCid", "Поверенные", (from c in dc.Clients orderby c.Name select new { Id = c.Cid, Name = c.Name }), -1, "order.ivp/findclients", "client.ivp/select?reset=true"));

        fc.Add(new DateFilterCondition<Order>("deadline", "Срок"));
        fc.Add(new NumericFilterCondition<Order>("taskid", "Номер записи на прием"));
        fc.Add(new BoolFilterCondition<Order>("iscomplicated", "Сложный документ"));
        fc.Add(new BoolFilterCondition<Order>("isinternal", "Внутренний документ"));

        fc.Add(new BoolFilterCondition<Order>("canbeassign", "С правом передоверия"));
        fc.Add(new BoolFilterCondition<Order>("isnotforregistration", "Сделать, но не регистрировать"));
        fc.Add(new BoolFilterCondition<Order>("isrush", "Срочный документ"));

        fc.Add(new NumericFilterCondition<Order>("doccount", "Количество оригиналов"));
        fc.Add(new NumericFilterCondition<Order>("xeroxcount", "Ксерокс"));
        fc.Add(new NumericFilterCondition<Order>("copycount", "Количество копий"));

        fc.Add(new BoolFilterCondition<Order>("isprepayment", "Аванс"));
        fc.Add(new BoolFilterCondition<Order>("iscashless", "Безнал"));
        fc.Add(new BoolFilterCondition<Order>("ispaid", "Оплачено"));
        fc.Add(new BoolFilterCondition<Order>("isnotonform", "Не на бланке"));
        fc.Add(new ManualFilterCondition<Order>("manual", "Дополнительно"));
    }
    #endregion

    #region Поиск
    protected override IQueryable<Order> Search(IQueryable<Order> query)
    {
        int oid;
        var searchStr = State["tb_search"] != null ? State["tb_search"].Trim() : "";
        if (!string.IsNullOrEmpty(searchStr))
        {
            //поиск по идентификатору заявки
            if ((Int32.TryParse(searchStr, out oid)) && oid > 0)
                return query.Where(q => q.Id == oid);
            //поиск по номеру реестра, если есть '-' и оно одно
            else if (searchStr.IndexOf('-') > -1 && searchStr.IndexOf('-') == searchStr.LastIndexOf('-'))
            {
                string[] arr = searchStr.Split('-');
                int rowNumber;
                Int32.TryParse(arr[1], out rowNumber);
                return query.Where(q => (q.NotarialRegisterId != null && q.NotarialRegisterRow != null) && q.NotarialRegister.Name == arr[0] && q.NotarialRegisterRow == rowNumber);
            }
            else
            {
                var search = String.Format("%{0}%", searchStr.Trim().ToLower());
                var startSearch = String.Format("{0}%", searchStr.Trim().ToLower());
                return query.Where(q =>
                                        SqlMethods.Like(q.Description, search)
                                        || SqlMethods.Like(q.Search, search)
                                        || SqlMethods.Like(q.NotarialAction.Name, startSearch)
                                        || q.OrderClients.Any(oc => SqlMethods.Like(oc.Name, startSearch))
                                            //|| q.OrderResponsibles.Any(or => SqlMethods.Like(or.Name, search))
                                            //|| q.OrderHistories.Any(oh => SqlMethods.Like(oh.Comment, search))
                                        //|| q.NotarialFormNumber.Any(fn => SqlMethods.Like(fn.Name, search))
                    //to do замут с временем блокировки
                                   ).Distinct();
            }
        }
        return query;
    }
    #endregion

    public void SavePaidAttributes()
    {
        int id;
        string idStr = Request["orderid"];
        if (!String.IsNullOrEmpty(idStr) && Int32.TryParse(idStr, out id) && id > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var order = dc.Orders.FirstOrDefault(o => o.Id == id);
                if (order != null)
                {
                    RequestHelper.InitializeFromRequest<Order>(Request, order);
                    DeleteFormNumbers(dc, order);
                    SaveFormNumbers(dc, order);
                    order.Operation = "Оплата";
                    dc.SubmitChanges();

                    if (order.IsPaid)
                        Logger.WriteLog(dc, LogOperationType.OrderPaid, (Session["user"] as User).Id, order.Id, order.StatusId);
                }
            }
        }
    }

    [AllowedRole("IsAdmin")]
    public void LoadPersonOrders()
    {
        _ActionKey = Action;
        ViewData["hidecb"] = true;

        TemplatePath = "~/views/person/personorder.haml";
        LayoutPath = "none";

        ProcessTable();
    }

    [AllowedRole("IsAdmin")]
    public void LoadCompanyOrders()
    {
        _ActionKey = Action;
        ViewData["hidecb"] = true;

        TemplatePath = "~/views/company/companyorder.haml";
        LayoutPath = "none";

        ProcessTable();
    }

    public void CreateFromTask(string taskid)
    {
        int taskId;

        if (!string.IsNullOrEmpty(taskid) && Int32.TryParse(taskid, out taskId))
            parentTaskId = taskId;

        LayoutPath = "~/views/layouts/popup.haml";

        Create();
    }
}